/**
 * @author harry
 * @date 2022/3/24/11:05
 */

package com.serviceOrchestration.aop;

import com.serviceOrchestration.annotation.SaveLog;
import com.serviceOrchestration.dao.LogDao;
import com.serviceOrchestration.pojo.CommonLog;
import com.serviceOrchestration.util.ContextHolder;
import com.serviceOrchestration.util.IPUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

@Aspect
@Component
@Slf4j
public class LogAspect {
    private final LogDao logDao;

    @Autowired
    public LogAspect(LogDao logDao) {
        this.logDao = logDao;
    }

    @Pointcut("@annotation(com.serviceOrchestration.annotation.SaveLog)")
    public void logPoint() {}

    /**
     * 1.抛出异常：执行finally记录日志，然后抛出异常，不会执行return<br/>
     * 2.没有异常：执行finally记录日志，执行return正常返回
     */
    @Around("logPoint()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        Throwable exception = null;
        long start = System.currentTimeMillis();
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            exception = e;
            throw e;
        } finally {
            long end = System.currentTimeMillis();
//            Throwable finalException = exception;
            saveLog(joinPoint, start, end, exception);
        }
        return result;
    }

    private void saveLog(ProceedingJoinPoint point, long start, long end, Throwable exception) {
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        SaveLog annotation = method.getAnnotation(SaveLog.class);
        HttpServletRequest request = ContextHolder.getRequest();

        assert request != null;
        String id = (String) request.getAttribute("sub");
        String role = (String) request.getAttribute("role");
        String operation = annotation.value();
        String methodName = method.getDeclaringClass().getName() + "." + method.getName();
        String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);
        Object[] paramValues = point.getArgs();
        StringBuilder paramsBuilder = null;
        if(paramNames != null && paramNames.length > 0) {
            paramsBuilder = new StringBuilder();
            for(int i = 0; i < paramNames.length; ++i) {
                paramsBuilder.append(paramNames[i]).append(":").append(paramValues[i]).append("; ");
            }
        }
        String params = paramsBuilder == null ? null : paramsBuilder.toString();
        long duration = end - start;
        String ip = IPUtil.getSourceIP();

        CommonLog newLog = new CommonLog(id, role, operation, methodName, params,
                new Date(start), duration, exception == null ? null : exception.getMessage(), ip);
        logDao.insert(newLog);
    }
}
